package zlog

import (
	"context"
	"fmt"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

const loggerKeyForGin = "Zlog"

// contextKey is how we find Loggers in a context.Context.
type contextKey struct{}

// 先从传入的 ctx 提取 logger，或者拿全局 logger，
// 然后把 keyvals 附加到 logger 上，把 logger 挂到 ctx 中，返回新的 ctx
func WithContext(ctx context.Context, keyvals ...any) context.Context {
	if l := FromContext(ctx); l != nil {
		return l.(*zapLogger).WithContext(ctx, keyvals...)
	}

	return std.WithContext(ctx, keyvals...)
}

// 把 logger 附加 keyvals 后，挂到 ctx 中，返回新的 ctx
func (l *zapLogger) WithContext(ctx context.Context, keyvals ...any) context.Context {
	// 用于把 logger 附加到一个新的 context 中
	with := func(l Logger) context.Context {
		return context.WithValue(ctx, contextKey{}, l)
	}

	// 如果是 gin 环境，就把 logger 附加到 gin.Context 中
	if c, ok := ctx.(*gin.Context); ok {
		with = func(l Logger) context.Context {
			c.Set(loggerKeyForGin, l)
			return c
		}
	}

	keylen := len(keyvals)
	if keylen == 0 || keylen%2 != 0 {
		return with(l)
	}

	data := make([]zap.Field, 0, (keylen/2)+1)
	for i := 0; i < keylen; i += 2 {
		data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
	}

	return with(l.With(data...))
}

// 从 ctx 中提取出 logger，没有就拿全局 logger，并给 logger 附加传入的 key-value
func FromContext(ctx context.Context, keyvals ...any) Logger {
	var key any = contextKey{}
	if _, ok := ctx.(*gin.Context); ok {
		key = loggerKeyForGin
	}

	// 默认拿全局单例 logger
	var log Logger = std
	// 尝试从 ctx 中拿到 logger
	if ctx != nil {
		if logger, ok := ctx.Value(key).(Logger); ok {
			log = logger
		}
	}

	// 传入的 key-value 必须是双数配对
	keylen := len(keyvals)
	if keylen == 0 || keylen%2 != 0 {
		return log
	}
	// 将传入的 key-value 转换成 zap.Field
	data := make([]zap.Field, 0, (keylen/2)+1)
	for i := 0; i < keylen; i += 2 {
		data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
	}

	return log.With(data...)
}

// 从 ctx 中提取出 logger
func C(ctx context.Context) Logger {
	return FromContext(ctx).AddCallerSkip(-1)
}
